﻿using MeshPickerTool;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;

public class DrawRectangleMng : MonoBehaviour
{
    #region 单例

    public static DrawRectangleMng Instance
    {
        get
        {
            if (_instance != null) return _instance;

            GameObject go = new GameObject("DrawRectangleMng");
            _instance = go.AddComponent<DrawRectangleMng>();
            return _instance;
        }
    }

    private static DrawRectangleMng _instance;

    #endregion

    /// <summary>
    /// 目标物体
    /// </summary>
    public GameObject Target;

    /// <summary>
    /// 是否开始绘制框选线框的标识
    /// </summary>
    [HideInInspector]
    public bool DrawRectangle;
    [HideInInspector]
    public Material RectMat = null;

    public SelectBoxMng SelectBoxMng;

    /// <summary>
    /// 记下鼠标按下位置
    /// </summary>
    private Vector3 startPos = Vector3.zero;

    /// <summary>
    /// 记下鼠标按下位置
    /// </summary>
    private Vector3 endPos = Vector3.zero;

    /// <summary>
    /// 贴纸框
    /// </summary>
    public GameObject Iron;

    private Color rectColor;
    private Texture stickImg;
    private Transform stickerObj;

    private MeshPicker meshPicker;
    public bool IsStickMode;

    void Awake()
    {
        this.stickImg = Resources.Load<Texture>("Images/3");
        this.SelectBoxMng =
            Instantiate(Resources.Load<GameObject>("Prefabs/SelectBox"), GameObject.Find("Canvas").transform)
                .GetComponent<SelectBoxMng>();
        this.SelectBoxMng.gameObject.SetActive(false);

        this.Iron = GameObject.Instantiate(Resources.Load<GameObject>("Prefabs/Iron"));
        ResetPivot(this.Iron);
        this.Iron.SetActive(false);

        this.RectMat = new Material(Shader.Find("Sprites/Default"));
        this.RectMat.hideFlags = HideFlags.HideAndDontSave;
        this.RectMat.shader.hideFlags = HideFlags.HideAndDontSave;
        this.rectColor = new Color(165 / 255f, 1, 215 / 255f, 200 / 255f);
        this.RectMat.color = this.rectColor;

        this.meshPicker = this.Iron.GetComponent<MeshPicker>();
        this.Target = GameObject.Find("Target");
    }

    void Update()
    {
        if (Input.GetMouseButtonDown(0) && this.IsStickMode)
        {
            if (EventSystem.current.IsPointerOverGameObject())
            {
                return;
            }

            this.DrawRectangle = true;
            this.startPos = Input.mousePosition; //记录按下位置
        }

        if (Input.GetMouseButtonUp(0) && this.DrawRectangle && this.IsStickMode)
        {
            if (Vector3.Distance(Input.mousePosition, this.startPos) <= 0.3f)
            {
                this.DrawRectangle = false;
            }
            else
            {
                this.endPos = Input.mousePosition;

                this.CheckSelection(this.startPos, this.endPos);
                this.DrawRectangle = false;
            }
        }
    }

    /// <summary>
    /// 确定框选范围
    /// </summary>
    /// <param name="start"></param>
    /// <param name="end"></param>
    private void CheckSelection(Vector2 start, Vector2 end)
    {
        Vector2[] rectPoints = new Vector2[4];
        rectPoints[0] = start;
        rectPoints[1] = new Vector2(start.x, end.y);
        rectPoints[2] = new Vector2(end.x, start.y);
        rectPoints[3] = end;

        rectPoints = SortPointsByClockwise(rectPoints);

        this.SelectBoxMng.gameObject.SetActive(true);
        this.SelectBoxMng.InitPointsPos(rectPoints);

        this.FlashIron(rectPoints);
    }

    public void FlashIronBySelectBox()
    {
        this.InitStickImg(this.stickImg);
    }

    /// <summary>
    /// 根据矩形刷新贴纸
    /// </summary>
    /// <param name="rectPoints"></param>
    public void FlashIron(Vector2[] rectPoints)
    {
        Vector3 screenSpace = Camera.main.WorldToScreenPoint(this.Target.transform.position);

        Vector3 worldStartPos =
            Camera.main.ScreenToWorldPoint(new Vector3(rectPoints[0].x, rectPoints[0].y, screenSpace.z));
        Vector3 worldOffset1Pos =
            Camera.main.ScreenToWorldPoint(new Vector3(rectPoints[1].x, rectPoints[1].y, screenSpace.z));
        Vector3 worldEndPos =
            Camera.main.ScreenToWorldPoint(new Vector3(rectPoints[2].x, rectPoints[2].y, screenSpace.z));
        Vector3 worldOffset2Pos =
            Camera.main.ScreenToWorldPoint(new Vector3(rectPoints[3].x, rectPoints[3].y, screenSpace.z));

        Vector3 centerPos = (worldEndPos + worldStartPos) / 2.0f;
        Vector3 planeNormal = GetPlaneNormal(worldStartPos, worldEndPos, worldOffset1Pos);
        this.SetIronLookAtNormal(planeNormal);
        this.Iron.transform.position = centerPos - this.Iron.transform.forward * 10;
        this.Iron.transform.localScale = new Vector3(Vector3.Distance(worldStartPos, worldOffset1Pos),
            Vector3.Distance(worldStartPos, worldOffset2Pos),
            50);
        this.InitStickImg(this.stickImg);
    }

    /// <summary>
    /// 生成贴纸
    /// </summary>
    public void InitStickImg(Texture img)
    {
        this.stickImg = img;
        Mesh mesh = this.meshPicker.Pick(this.Target.GetComponent<MeshFilter>());
        mesh.RecalculateBounds();

        if (this.stickerObj)
        {
            GameObject.Destroy(this.stickerObj.gameObject);
        }

        this.stickerObj = new GameObject("StickMesh").transform;
        this.stickerObj.position = this.Target.transform.position;
        this.stickerObj.rotation = this.Target.transform.rotation;
        this.stickerObj.localScale = this.Target.transform.localScale;
        this.stickerObj.SetParent(this.Target.transform);
        this.stickerObj.gameObject.AddComponent<MeshFilter>().sharedMesh = mesh;
        Material mat = new Material(Shader.Find("Custom/ZFighting"));
        if (mat.HasProperty("_OffsetFactor"))
        {
            mat.SetFloat("_OffsetFactor", -1);
        }

        if (mat.HasProperty("_MainTex"))
        {
            mat.SetTexture("_MainTex", img);
        }

        this.stickerObj.gameObject.AddComponent<MeshRenderer>().material = mat;
    }

    private void OnGUI()
    {
        if (this.DrawRectangle)
        {
            Vector3 end = Input.mousePosition; //鼠标当前位置
            GL.PushMatrix(); //保存摄像机变换矩阵,把投影视图矩阵和模型视图矩阵压入堆栈保存
            if (!this.RectMat)
                return;
            this.RectMat.SetPass(0); //为渲染激活给定的pass。
            GL.LoadPixelMatrix(); //设置用屏幕坐标绘图
            GL.Begin(GL.QUADS); //开始绘制矩形
            GL.Color(new Color(this.rectColor.r, this.rectColor.g, this.rectColor.b, 0.1f)); //设置颜色和透明度，方框内部透明

            //绘制顶点
            GL.Vertex3(this.startPos.x, this.startPos.y, 0);
            GL.Vertex3(end.x, this.startPos.y, 0);
            GL.Vertex3(end.x, end.y, 0);
            GL.Vertex3(this.startPos.x, end.y, 0);
            GL.End();

            GL.Begin(GL.LINES); //开始绘制线
            GL.Color(this.rectColor); //设置方框的边框颜色 边框不透明
            GL.Vertex3(this.startPos.x, this.startPos.y, 0);
            GL.Vertex3(end.x, this.startPos.y, 0);
            GL.Vertex3(end.x, this.startPos.y, 0);
            GL.Vertex3(end.x, end.y, 0);
            GL.Vertex3(end.x, end.y, 0);
            GL.Vertex3(this.startPos.x, end.y, 0);
            GL.Vertex3(this.startPos.x, end.y, 0);
            GL.Vertex3(this.startPos.x, this.startPos.y, 0);
            GL.End();

            GL.PopMatrix(); //恢复摄像机投影矩阵

        }
    }

    /// <summary>
    /// 设置贴纸框朝向面法线方向
    /// </summary>
    /// <param name="planeNormal">面法线方向</param>
    private void SetIronLookAtNormal(Vector3 planeNormal)
    {
        Vector3 camNormal = Camera.main.transform.forward;
        float angle = Vector3.Angle(camNormal, planeNormal);
        if (angle < 90)
        {
            this.Iron.transform.forward = planeNormal;
        }
        else
        {
            this.Iron.transform.forward = -planeNormal;
        }

        //Debug.DrawLine(centerPos, centerPos + GetPlaneNormal(worldStartPos, worldEndPos, worldOffset1Pos) * 100, Color.red, 10);
    }

    /// <summary>
    /// 将给定点集顺时针排序
    /// </summary>
    /// <param name="pts_src">四边形四个顶点组成的点集</param>
    /// <returns></returns>
    public static Vector2[] SortPointsByClockwise(Vector2[] pts_src)
    {
        if (pts_src.Length != 4) return null; //确保为四边形

        //求四边形中心点？坐标
        float x_average = 0;
        float y_average = 0;
        float x_sum = 0;
        float y_sum = 0;
        for (int i = 0; i < 4; i++)
        {
            x_sum += pts_src[i].x;
            y_sum += pts_src[i].y;
        }

        x_average = x_sum / 4;
        y_average = y_sum / 4;
        Vector2 center = new Vector2(x_average, y_average);

        Vector2[] result = new Vector2[4];
        for (int i = 0; i < 4; i++)
        {
            if (pts_src[i].x < center.x && pts_src[i].y < center.y)
            {
                result[0] = pts_src[i]; //左上角点
            }

            if (pts_src[i].x > center.x && pts_src[i].y < center.y)
            {
                result[1] = pts_src[i]; //右上角点
            }

            if (pts_src[i].x > center.x && pts_src[i].y > center.y)
            {
                result[2] = pts_src[i]; //右下角点
            }

            if (pts_src[i].x < center.x && pts_src[i].y > center.y)
            {
                result[3] = pts_src[i]; //左下角点
            }
        }

        return result;
    }

    /// <summary>
    /// 重设Iron的中心到前面中心
    /// </summary>
    /// <param name="go"></param>
    /// <returns></returns>
    private static Vector3 ResetPivot(GameObject go)
    {
        Mesh mesh = go.GetComponent<MeshFilter>().mesh;
        Bounds bounds = mesh.bounds;
        Vector3 offset = -1 * bounds.center;
        Vector3 originalPivot = new Vector3(offset.x / bounds.extents.x, offset.y / bounds.extents.y,
            offset.z / bounds.extents.z);
        Vector3 newPivot = Vector3.back;
        Vector3 diff = Vector3.Scale(bounds.extents, originalPivot - newPivot);
        //go.transform.position -= Vector3.Scale(diff, go.transform.localScale);
        //this.transform.position = this.data.Position;
        //this.transform.rotation = this.data.Rotation;
        //this.transform.localScale = this.data.LocalScale;

        Vector3[] verts = mesh.vertices;
        for (int i = 0; i < verts.Length; i++)
        {
            verts[i] += diff;
        }

        mesh.vertices = verts;
        mesh.RecalculateBounds();

        return diff;
    }

    /// <summary>
    /// 返回三个顶点所在平面的法向量
    /// </summary>
    /// <param name="p1"></param>
    /// <param name="p2"></param>
    /// <param name="p3"></param>
    /// <returns></returns>
    private static Vector3 GetPlaneNormal(Vector3 p1, Vector3 p2, Vector3 p3)
    {
        float a = ((p2.y - p1.y) * (p3.z - p1.z) - (p2.z - p1.z) * (p3.y - p1.y));
        float b = ((p2.z - p1.z) * (p3.x - p1.x) - (p2.x - p1.x) * (p3.z - p1.z));
        float c = ((p2.x - p1.x) * (p3.y - p1.y) - (p2.y - p1.y) * (p3.x - p1.x));

        return new Vector3(a, b, c);
    }
}
